<?php
namespace hid\base;

use Exception;
use hid\IdManager;

/**
 * ID生成器基类
 *<B>说明：</B>
 *<pre>
 * 略
 *</pre>
 */
class IdBuilder
{
    /**
     * id名称(唯一标识)
     * @var string
     */
    protected $name = '';

    /**
     * 步长
     * @var int
     */
    protected $step = 1;

    /**
     * id存储器类型
     * @var string
     */
    protected $storage;

    /**
     * @var IdStorage
     */
    protected $storageCache;

    /**
     * 如果获取id失败,等待超时时间,默认3秒,单位毫秒
     * @var int
     */
    protected $waitTimeout = 3000;

    /**
     * 等待时间间隔
     * @var int
     */
    protected $waitSleepTime = 20;

    /**
     * 获取id失败是否抛出异常
     * @var bool
     */
    protected $failException =  false;

    /**
     * 是否初始化值，默认关闭主动设置初始值功能
     * @var bool
     */
    protected $isInitValue = false;

    /**
     * 初始化值
     * @var int
     */
    protected $initValue = 1;

    /**
     *  ID管理器
     * @var IdManager;
     */
    protected $idManager;

    /**
     * name 缓存
     * @var string
     */
    protected $storeKey = '';

    public function __construct(array $config = [])
    {
        if (!empty($config)) {
            foreach ($config as $key => $value) {
                $this->$key = $value;
            }
        }
    }

    public function getInitValue():int
    {
        return $this->initValue;
    }

    public function isInitValue():bool
    {
        return $this->isInitValue;
    }

    public function getStorage():IdStorage
    {
        if (is_null($this->storageCache)) {
            $this->storageCache = $this->idManager->getIdStorage($this->storage);
        }

        return $this->storageCache;
    }

    public function getInitValueForStorage():int
    {
        return $this->initValue - $this->step;
    }

    public function setWaitTimeout(int $timeout,int $waitSleepTime):self
    {
        $this->waitTimeout = $timeout;
        $this->waitSleepTime = $waitSleepTime;

        return $this;
    }

    public function setFailException(bool $failException = true):self
    {
        $this->failException = $failException;

        return $this;
    }

    public function getStoreKey():string
    {
        if ($this->storeKey === '') {
            $name = $this->idManager->buildKey($this->name);
            $this->storeKey = $this->idManager->buildKey($this->getStorage()->getStoreKey(),['name'=>$name]);
        }

        return $this->storeKey;
    }

    public function setName(string $name):self
    {
        $this->name = $name;

        return $this;
    }

    public function getName():string
    {
        return $this->name;
    }

    public function setStep(int $step):self
    {
        $this->step = $step;

        return $this;
    }

    public function getStep():int
    {
        return $this->step;
    }

    public function setStorage(string $storage):self
    {
        $this->storage = $storage;

        return $this;
    }

    protected function getNextId():?int
    {
        $id = $this->getStorage()->getNextId($this);

        return $id;
    }

    /**
     * 获取下一个id
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @return string
     */
    public function getId():?string
    {
        $waitSleepTime = $this->waitSleepTime;
        $waitTimeout = $this->waitTimeout;
        $beginTime = microtime(true);
        $id = null;
        while (true) {
            $id = $this->getNextId();
            if (!is_null($id)) {
                break;
            }

            // 继续等待
            if ($waitTimeout > 0  && (microtime(true) - $beginTime) < ($waitTimeout / 1000)) {
                usleep($waitSleepTime * 1000);
            } else {
                break;
            }
        }

        if (is_null($id) && $this->failException === true) {
            throw new Exception('get id fail');
        }

        return is_null($id) ? null : (string)$id;
    }
}


